home *** CD-ROM | disk | FTP | other *** search
/ ftp.mactech.com 2010 / ftp.mactech.com.tar / ftp.mactech.com / machack / Hacks97 / NewsTicker.sit / NewsTicker / source code / Extractors / HTMLExtractor.cp < prev    next >
Text File  |  1997-06-27  |  12KB  |  599 lines

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    NewsTicker, my Hack for 1997
  4. #
  5. #    HTMLExtractor.cp    -    Base class to read an HTML page in, and parse
  6. #                            out the interesting stuff.  Useless on its own,
  7. #                            only exists to be derived..
  8. #
  9. ------------------------------------------------------------------------------*/
  10. #include <Threads.h>
  11. #include <strings.h>
  12.  
  13. #include "HTMLExtractor.h"
  14. #include "SubWooferEndPoint.h"
  15. #include "HTTPEndPoint.h"
  16. #include "Idler.h"
  17. #include "TickerGlobals.h"    //get our structures and all
  18. #include "TickerWindowHandler.h"
  19. #include "BeachBall.h"
  20.  
  21. #include <string.h>
  22.  
  23. BeachBall*    gTheBall = nil;
  24.  
  25. class TickerIdler : public Idler
  26. {
  27.     private:
  28.         long            mlWNEDelay;
  29.         HTMLExtractor*    mftheExtractor;
  30.      
  31.     protected:
  32.                         TickerIdler        (const TickerIdler& oRHS);
  33.         TickerIdler&    operator=        (const TickerIdler& oRHS);
  34.  
  35.     public:
  36.                         TickerIdler        (HTMLExtractor*    theExtractor);
  37.         virtual void    YieldAction        (void);
  38.         virtual            ~TickerIdler    (void)    {    }
  39. };
  40. //
  41. // The tickler below does many things while waiting for data to send/receive from
  42. // the Internet.  It spins a beachball, and recognizes as we come to/from the background,
  43. // and it scrolls our window bellow us.  It also recognizes command-period or closing the
  44. // window to abort a read/write
  45. //
  46. #define kDefaultWNEDelay    15
  47. #define kDefaultIdlerPeriod    15
  48.  
  49. TickerIdler::TickerIdler (HTMLExtractor*    theExtractor)        //default constructor.  Tell the Idler base class how often to call yield action
  50. {
  51.     SetPeriod (kDefaultWNEDelay);
  52.  
  53.     mlWNEDelay = kDefaultIdlerPeriod;
  54.     mftheExtractor = theExtractor;
  55. }
  56.  
  57. void TickerIdler::YieldAction (void)
  58. {
  59.     EventRecord        sEvent;
  60.     
  61.     JustHandleWindow();
  62.     
  63. #ifdef USESUBWOOFER
  64.     YieldToAnyThread();
  65. #endif
  66.     
  67.     if ((gTheBall!=nil)&&(!gInBackground))
  68.         gTheBall->Idle();        //spin our beach ball cursor
  69.     
  70.     if (!gDoneFlag)
  71.     {
  72.         if (mftheExtractor)
  73.             mftheExtractor->Cancel();
  74.     }
  75.     
  76.     if (WaitNextEvent(everyEvent , &sEvent, mlWNEDelay, nil))
  77.     {
  78.         switch (sEvent.what)
  79.         {
  80.             case kHighLevelEvent:
  81.                 AEProcessAppleEvent( &sEvent ) ;
  82.                 break;
  83.             case keyDown:
  84.                 if (((sEvent.message & charCodeMask)=='.')&&(sEvent.modifiers & cmdKey))
  85.                 {
  86.                     if (mftheExtractor)
  87.                         mftheExtractor->Cancel();
  88.                 }
  89.                 break;
  90.             case osEvt:
  91.                 if (((sEvent.message >> 24) & 0x0FF) == kSuspendResumeMessage) /* high byte of message */
  92.                 {
  93.                     gInBackground = (sEvent.message & kResumeMask) == 0;
  94.                 }
  95.         }
  96.     }
  97. }
  98.  
  99. HTMLExtractor::HTMLExtractor        (char* theaddress, short theIconID, sMyDataPtr theDataPtr)
  100. {
  101. #ifdef USESUBWOOFER
  102.     mfWebPipe = nil;
  103. #else
  104.     mfHTTPPipe = nil;
  105. #endif
  106.     mfDoingARead = false;
  107.     mfTheDataPtr = theDataPtr;
  108.     mfLastModified[0] = 0;
  109.     mfIconID = theIconID;
  110.     
  111.     strcpy(mfAddress, theaddress);
  112. }
  113.  
  114. HTMLExtractor::~HTMLExtractor        (void)
  115. {
  116. #ifdef USESUBWOOFER
  117.     if (mfWebPipe)
  118.     {
  119.         delete mfWebPipe; mfWebPipe = nil;
  120.     }
  121. #else
  122.     if (mfHTTPPipe)
  123.     {
  124.         delete mfHTTPPipe; mfHTTPPipe = nil;
  125.     }
  126. #endif
  127. }
  128.  
  129. void HTMLExtractor::AddEntry(Str255 theSubject, Str255 theURL)
  130. {
  131.     if (gThePrefs.JustShowFirstThree&&(mfTempHeadlineCount>=3))    //demo mode
  132.     {
  133.         mfDoingARead = false;
  134.         return;
  135.     }
  136.     
  137.     if (mfTempHeadlineCount<tempmaxHeadlines)
  138.     {
  139.         PLstrcpy(mfTempHeadlines[mfTempHeadlineCount].Subject, theSubject);
  140.         PLstrcpy(mfTempHeadlines[mfTempHeadlineCount].URL, theURL);
  141.         mfTempHeadlines[mfTempHeadlineCount].cicnResID = mfIconID;
  142.         
  143.         mfTempHeadlineCount++;
  144.     }
  145. }
  146.  
  147. // Called by base app to read all entries in, or check header and see if it's changed
  148. void HTMLExtractor::ReadEntries        (void)
  149. {
  150.     short    index;
  151.     short    destindex;
  152.     TickerIdler* theidler = new TickerIdler(this);
  153.     Ptr        thebuffer;
  154.     long    buffersize;
  155.     OSErr    io;
  156.     
  157.     mfDoingARead = true;
  158.     mfReadingHeader = true;
  159.     thetextsize = 0;
  160.     thetagsize = 0;
  161.     AmOnTag = false;
  162.     mfTempHeadlineCount = 0;
  163.     
  164.     if (!gTheBall)
  165.         gTheBall = new BeachBall();
  166.  
  167. // Use the subwoofer code
  168.  
  169. #ifdef USESUBWOOFER
  170.     mfReadingHeader = false;    //we don't get headers from Subwoofer
  171.     if (mfWebPipe)
  172.     {
  173.         delete mfWebPipe; mfWebPipe = nil;
  174.     }
  175.  
  176.     mfWebPipe = new SubWooferEndPoint(this);
  177.     if (mfWebPipe->StartGettingFile(mfAddress, 80, theidler)!=noErr)
  178.     {
  179.         delete mfWebPipe; mfWebPipe = nil;
  180.         delete theidler;
  181.         return;
  182.     }
  183.     do
  184.     {
  185.         mfWebPipe->DoIdle();
  186.         theidler->YieldAction();
  187.     }
  188.     while (mfDoingARead);
  189.     
  190.     io =     mfWebPipe->GetSubWoofHeader(mfLastModified);
  191.  
  192.     
  193.     delete mfWebPipe;
  194.     mfWebPipe = nil;
  195. #else     
  196. //
  197. // Use the raw OT stuff
  198.     if (mfHTTPPipe)
  199.     {
  200.         delete mfHTTPPipe; mfHTTPPipe = nil;
  201.     }
  202.  
  203.     mfHTTPPipe = new HTTPEndPoint(this);
  204.     if (mfHTTPPipe->StartGettingFile(mfAddress, 80, theidler)!=noErr)
  205.     {
  206.         delete mfHTTPPipe; mfHTTPPipe = nil;
  207.         delete theidler;
  208.         return;
  209.     }
  210.     do
  211.     {
  212.         mfHTTPPipe->DoIdle();
  213.         theidler->YieldAction();
  214.     }
  215.     while (mfDoingARead);
  216.     
  217.     delete mfHTTPPipe;
  218.     mfHTTPPipe = nil;
  219. #endif
  220.      delete theidler;
  221.     
  222.     // Delete all entries with cicnResID = mfIconID
  223.     destindex = 0;
  224.     for (index = 0; index < mfTheDataPtr->MsgCount; index++)
  225.     {
  226.         if (mfTheDataPtr->theHeadlines[index].cicnResID!=mfIconID)    //don't delete it
  227.         {
  228.             if (index!=destindex)    //copy down if we need to
  229.             {
  230.                 mfTheDataPtr->theHeadlines[destindex] = mfTheDataPtr->theHeadlines[index];
  231.             }
  232.             destindex++;
  233.         }
  234.     }
  235.     mfTheDataPtr->MsgCount = destindex;
  236.     
  237.     // Now copy the entries we accumulated out
  238.     for (index = 0; index<mfTempHeadlineCount; index++)    // copy the entries off
  239.     {
  240.         if (mfTheDataPtr->MsgCount<maxHeadlines)
  241.         {
  242.             mfTheDataPtr->theHeadlines[mfTheDataPtr->MsgCount] = mfTempHeadlines[index];
  243.             mfTheDataPtr->MsgCount++;
  244.         }
  245.     }
  246. }
  247.  
  248. // Called by endpoint as it gets strings
  249. void HTMLExtractor::ReceiveString    (char* string, short numchars)
  250. {
  251.     short    index;
  252.     char    thechar;
  253.     
  254.     if (mfReadingHeader)
  255.     {
  256.         if (numchars <= 2) //must be crlf
  257.             mfReadingHeader = false;
  258.         else
  259.         {
  260.             //if Last-modifed line, save it
  261.             if    (MyCompareStr(string, "Last-Modified:"))
  262.             {
  263.                 if (numchars>31)
  264.                     numchars = 31;
  265.                 mfLastModified[0] = numchars;
  266.                 BlockMove(string, &mfLastModified[1], numchars);
  267.             }
  268.         }
  269.     }
  270.     else
  271.     {
  272.         for (index = 0; index<numchars; index++)
  273.         {
  274.             thechar = string[index];
  275.             if ((thechar==0x0d)||(thechar==0x0a)||(thechar==0x09))//make carriage returns and line feeds spaces
  276.                 thechar = ' ';
  277.             if (AmOnTag)
  278.             {
  279.                 if ((thetagsize<2047)&&((thetagsize>0)||(thechar!=' ')))    //add this character to the tag
  280.                 {
  281.                     thetag[thetagsize] = thechar; thetagsize++;
  282.                 }
  283.                 if (thechar=='>')    //end of tag?
  284.                 {
  285.                     thetag[thetagsize] = 0;    //make it a nice C string
  286.                     HandleToken(thetag, thetagsize, true);    //and handle it
  287.                     thetextsize = 0;    //And star getting text
  288.                     AmOnTag = false;
  289.                 }
  290.             }
  291.             else
  292.             {
  293.                 if (thechar=='<')    //start of tag?
  294.                 {
  295.                     if (thetextsize>0)    //any text to handle?
  296.                     {
  297.                         thetext[thetextsize] = 0;
  298.                         HandleToken(thetext, thetextsize, false);    //handle the text
  299.                     }
  300.                     thetag[0] = thechar;    //put this in the tag and start parsing it
  301.                     thetagsize = 1;
  302.                     AmOnTag = true;
  303.                 }
  304.                 else    //nope, just add to the text
  305.                 {
  306.                     if ((thetextsize<2047)&&((thetextsize>0)||(thechar!=' ')))
  307.                     {
  308.                         thetext[thetextsize] = thechar; thetextsize++;
  309.                     }
  310.                 }
  311.             }
  312.         }
  313.     }
  314. }
  315.         
  316. void HTMLExtractor::HandleToken(char* string, short numchars, Boolean isCommand)
  317. {
  318.  
  319. }
  320.         
  321. void HTMLExtractor::Disconnect(void)
  322. {
  323.     mfDoingARead = false;
  324. }
  325.  
  326. // Cancel the connection
  327. //        
  328. void HTMLExtractor::Cancel(void)
  329. {
  330.     mfDoingARead = false;
  331. }
  332.  
  333. // Called by base app to read the header in
  334. void HTMLExtractor::ReadLastModified(void)
  335. {
  336.     TickerIdler* theidler = new TickerIdler(this);
  337.     Ptr        thebuffer;
  338.     long    buffersize;
  339.     OSErr    io;
  340.     
  341.     mfDoingARead = true;
  342.     mfReadingHeader = true;
  343.     thetextsize = 0;
  344.     thetagsize = 0;
  345.     AmOnTag = false;
  346.     mfTempHeadlineCount = 0;
  347.     
  348.     if (!gTheBall)
  349.         gTheBall = new BeachBall();
  350.  
  351.  
  352. // Use the subwoofer code
  353.  
  354. #ifdef USESUBWOOFER
  355.     if (mfWebPipe)
  356.     {
  357.         delete mfWebPipe; mfWebPipe = nil;
  358.     }
  359.  
  360.     mfWebPipe = new SubWooferEndPoint(this);
  361.     if (mfWebPipe->StartGettingHeader(mfAddress, 80, theidler)!=noErr)
  362.     {
  363.         delete mfWebPipe; mfWebPipe = nil;
  364.         delete theidler;
  365.         return;
  366.     }
  367.     do
  368.     {
  369.         mfWebPipe->DoIdle();
  370.         theidler->YieldAction();
  371.     }
  372.     while (mfDoingARead);
  373.     io =     mfWebPipe->GetSubWoofHeader(mfLastModified);
  374.     
  375.     delete mfWebPipe;
  376.     mfWebPipe = nil;
  377. #else     
  378. //
  379. // Use the raw OT stuff
  380.     if (mfHTTPPipe)
  381.     {
  382.         delete mfHTTPPipe; mfHTTPPipe = nil;
  383.     }
  384.  
  385.     mfHTTPPipe = new HTTPEndPoint(this);
  386.     if (mfHTTPPipe->StartGettingHeader(mfAddress, 80, theidler)!=noErr)
  387.     {
  388.         delete mfHTTPPipe; mfHTTPPipe = nil;
  389.         delete theidler;
  390.         return;
  391.     }
  392.     do
  393.     {
  394.         mfHTTPPipe->DoIdle();
  395.         theidler->YieldAction();
  396.     }
  397.     while (mfDoingARead);
  398.     
  399.     delete mfHTTPPipe;
  400.     mfHTTPPipe = nil;
  401. #endif
  402.      delete theidler;
  403.     
  404. }
  405.  
  406. void HTMLExtractor::GetLastModified    (Str31 LastModStr)
  407. {
  408.     PLstrcpy(LastModStr, mfLastModified);
  409. }
  410.  
  411. //
  412. // Here is some standard code to help parse the HTML
  413. //
  414. static char*    SkipWhiteChars(char* pcSrc)
  415. {
  416.     while ((*pcSrc != 0) && ((*pcSrc== ' ') || (*pcSrc == '\r') || (*pcSrc == '\n')))
  417.         pcSrc++;
  418.     return pcSrc;
  419. }
  420.  
  421. static char*    SkipWhiteCharsAndEqual(char* pcSrc)
  422. {
  423.     pcSrc = SkipWhiteChars(pcSrc);
  424.  
  425.     if (*pcSrc == '=')
  426.         pcSrc++;
  427.     pcSrc = SkipWhiteChars(pcSrc);
  428.  
  429.     return pcSrc;
  430. }
  431.  
  432. Boolean    MyCompareStr(char* p1, char* p2)
  433. {
  434.     short thelength = strlen(p2);
  435.     
  436.     return (IdenticalText (p1, p2, thelength, thelength, nil)==0);
  437. }
  438.  
  439. //
  440. // Look for some quoted data for a given marker
  441. //
  442. void FindATag(char* tag, char* theLink, char* theMarker)
  443. {
  444.     char*    cp;
  445.     
  446.     cp = theLink;
  447.     *cp  = 0;
  448.     
  449.     do
  450.     {
  451.         if (*tag != ' ')
  452.             return;
  453.         tag++;
  454.         
  455.         //DebugStr("\pPreparing to get the tag");
  456.         tag = ::SkipWhiteChars(tag);
  457.         if (::MyCompareStr(tag, theMarker))
  458.         {
  459.             tag += sizeof(theMarker);
  460.             tag = ::SkipWhiteCharsAndEqual(tag);
  461.  
  462.             if (*tag != '"')
  463.                 return;
  464.             tag++;
  465.  
  466.             if (*tag == '#')    //A navigation on same page link
  467.                 return;
  468.  
  469.             while ((*tag != 0) && (*tag != '"'))
  470.             {
  471.                 if (*tag=='?')    //restart, this was apple funkiness
  472.                 {
  473.                     cp = theLink;
  474.                     tag++;
  475.                 }
  476.                 else if (*tag=='$')    //another part of funkiness, this isn't a good link
  477.                 {
  478.                     *theLink = 0;
  479.                     return;
  480.                 }
  481.                 else *(cp++) = *(tag++);
  482.             }
  483.             *cp = 0;    //mark the end
  484.             return;
  485.         }
  486.         else
  487.         {
  488.             do    //Skip this item.  Get past the marker
  489.             {
  490.                 tag++;
  491.             }
  492.             while ((*tag!=0) && (*tag!='='));
  493.             
  494.             tag++;    //skip the =
  495.             if (*tag=='"')
  496.             {
  497.                 tag++;
  498.                 do    //Skip the quoted data
  499.                 {
  500.                     tag++;
  501.                 }
  502.                 while ((*tag!=0) && (*tag!='"'));
  503.             }
  504.             
  505.             do    //Skip the data, waiting for a space
  506.             {
  507.                 tag++;
  508.             }
  509.             while ((*tag!=0) && (*tag!=' ') && (*tag!='>'));
  510.         }
  511.     }
  512.     while ((*tag!='>')&&(*tag!=0));
  513. }
  514.  
  515. static void SaveHRef(char* tag, char* HTMLLink)
  516. {
  517.     char*    cp;
  518.     //See if it's A HREF="
  519.     cp = HTMLLink;
  520.     *cp  = 0;
  521.     
  522.     if (*tag != ' ')
  523.         return;
  524.     tag++;
  525.  
  526.     tag = ::SkipWhiteChars(tag);
  527.     if (!::MyCompareStr(tag, "HREF"))
  528.         return;
  529.     tag += 4;
  530.     tag = ::SkipWhiteCharsAndEqual(tag);
  531.  
  532.     if (*tag != '"')
  533.         return;
  534.     tag++;
  535.  
  536.     if (*tag == '#')    //A navigation on same page link
  537.         return;
  538.  
  539.     while ((*tag != 0) && (*tag != '"'))
  540.     {
  541.         if (*tag=='?')    //restart, this was apple funkiness
  542.         {
  543.             cp = HTMLLink;
  544.             tag++;
  545.         }
  546.         else *(cp++) = *(tag++);
  547.     }
  548.     *cp = 0;    //mark the end
  549. }
  550.  
  551. static Boolean isFullURL(char* theURL)
  552. {
  553.     for ( ; *theURL != 0; theURL++)
  554.         if (*theURL == ':')
  555.             return true;
  556.  
  557.     return false;
  558. }
  559.  
  560. Boolean HTMLExtractor::ParseGoodURL(char* thestring, Str255 theURL)
  561. {
  562.     char HTMLLink[256];
  563.     char headerstr[10] = "http://";
  564.     short index;
  565.  
  566.     theURL[0] = 0;
  567.     
  568.     //SaveHRef(thestring, HTMLLink);
  569.     
  570.     FindATag(thestring, HTMLLink, "HREF");
  571.     
  572.     if ((HTMLLink[0]==0)||(HTMLLink[0] == '#'))
  573.         return false;
  574.  
  575.     if (!isFullURL(HTMLLink))
  576.     {
  577.         for (index = 0; headerstr[index]!=0; index++)    //http://
  578.         {
  579.             theURL[0]++;theURL[theURL[0]] = headerstr[index];
  580.         }
  581.         for (index = 0; mfAddress[index]!=0; index++)    //add our address to it
  582.         {
  583.             theURL[0]++;theURL[theURL[0]] = mfAddress[index];
  584.         }
  585.         theURL[0]++;theURL[theURL[0]] = '/';
  586.     }
  587.  
  588.     index = 0;
  589.     if (HTMLLink[0] == '/')
  590.         index++;
  591.  
  592.     for ( ; HTMLLink[index] != 0; index++)
  593.     {
  594.         theURL[0]++;theURL[theURL[0]] = HTMLLink[index];
  595.     }
  596.  
  597.     return true;
  598. }
  599.